package com.zxl.openapi.api.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zxl.openapi.api.SearchServiceApi;
import com.zxl.openapi.utils.SearchUtil;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceApiImpl implements SearchServiceApi {

    Logger logger = LoggerFactory.getLogger(SearchServiceApiImpl.class);

    private String indexName = "1907_log_index";

    private String typeName = "1907_log_type";

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RestHighLevelClient client;

    @Override
    public void createIndex() throws Exception {

        if(!isExist(indexName)){

            CreateIndexRequest request = new CreateIndexRequest(indexName,
                    Settings.builder().
                            put("number_of_replicas",1).
                            put("number_of_shards",5)
                            .build());

            SearchUtil.buildMapping(typeName,request);

            client.indices().create(request, RequestOptions.DEFAULT);

            logger.error("创建index:{}成功" + indexName);

        } else {

            logger.error("index:{}已经存在" + indexName);

        }

    }

    @Override
    public boolean isExist(String index) throws Exception {

        GetIndexRequest getIndexRequest = new GetIndexRequest();

        getIndexRequest.indices(indexName);

        return client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);

    }

    @Override
    public void add(String json) throws Exception {

        IndexRequest indexRequest = new IndexRequest(indexName,typeName);

        indexRequest.source(json, XContentType.JSON);

        client.index(indexRequest,RequestOptions.DEFAULT);

        logger.error("es收到数据插入到索引中:{}", json);
    }


    /**
     * 可能会传递一个参数, 两个参数,三个参数,四个参数
     * 服务端在实现功能的基础来定义规则:我们说了算,客户端说的不算
     * 我们定义你要查询必须传递什么格式的数据比如说json 或者是key value类型的键值对,但是这两个只能选择一个
     * 此处我们要求必须传递json,而且json的key必须是我们es中 mapping的 name
     * @param params
     * @return
     */
    @Override
    public List<Map> search(String params){

        List<Map> result = new ArrayList<>();

        try {
            //讲传来的JSON转换为map
            Map map = objectMapper.readValue(params, Map.class);
            //构建searchSourceBuilder
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

            SearchUtil.buildSearchSource(searchSourceBuilder,map);
            //是不是有分页,我们规定 如果要分页查询 就必须传递 start和size两个参数
            Object start = map.get("start");
            //每页显示条数
            Object size = map.get("size");
            //如果没传递 那么赋予一个默认值
            if(start == null){

                start = 1;

            }

            if(size == null){

                size = 10;

            }

            searchSourceBuilder.from((Integer.parseInt(start.toString())-1)*Integer.parseInt(size.toString()));

            searchSourceBuilder.size(Integer.parseInt(size.toString()));
            //是不是有高亮?,如果要高亮,必须传递高亮的前缀和后缀
            String requestContent = (String) map.get("requestContent");
            //我们高亮的数据是查询到的requestcontent,如果用户没有查询数据,只是选择了范围,实际上是不需要高亮的
            if(requestContent != null){
                //如果传递了查询关键字的时候才会开启高亮
                //我们规定 你如果需要高亮,那么必须传递前缀和后缀过来,前缀和后缀的key 分别是highlightpretag  highlightposttag
                String highlightpretag = (String) map.get("highlightpretag");

                String highlightposttag = (String) map.get("highlightposttag");
                //用户可能会不传递,因为我们控制不了用户,实际上注意,有些东西就不是用户可以控制的
                if (highlightpretag == null||"".equalsIgnoreCase(highlightpretag.trim())) {

                    highlightpretag = "<span color='green'>";

                }

                if (highlightposttag == null||"".equalsIgnoreCase(highlightposttag.trim())) {

                    highlightposttag = "</span>";

                }

                HighlightBuilder highlightBuilder = new HighlightBuilder();

                highlightBuilder.field("requestContent");

                highlightBuilder.preTags(highlightpretag);

                highlightBuilder.postTags(highlightposttag);
                //添加高亮的查询条件
                searchSourceBuilder.highlighter(highlightBuilder);

            }

            SearchRequest request = new SearchRequest(indexName,typeName);

            request.source(searchSourceBuilder);

            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //获取命中的数据封装对象
            SearchHits hits = response.getHits();
            //获取命中的数据
            SearchHit[] hitsHits = hits.getHits();

            for (SearchHit hitsHit : hitsHits) {
                //获取当前数据的json格式内容
                String sourceAsString = hitsHit.getSourceAsString();
                //查询到的是默认的内容,也就是不包含高亮数据的原始内容
                Map data = objectMapper.readValue(sourceAsString, Map.class);
                //获取高亮数据
                Map<String, HighlightField> highlightFields = hitsHit.getHighlightFields();
                //获取当前需要高亮的字段的高亮内容
                HighlightField highlightField = highlightFields.get("requestContent");
                //当前数据很有可能会有高亮内容,判断一下,如果有就把高亮数据替换掉原始的内容
                if(highlightField != null){
                    Text[] fragments = highlightField.getFragments();

                    if(fragments != null){
                        //拿到真正的高亮内容
                        String s = fragments[0].toString();
                        //替换掉原始内容
                        data.put("requestContent",s);

                    }
                }

                result.add(data);

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

        return result;
    }
}
